/**
* 
* FP avec fifo par lvl de priorité
* fifo sur transition vers ready 
* preemptif 
* c'est à dire que l'on gére d'abord par lvl de priorité puis par ancienneté d'arrivé dans la liste
* see <a href="{@docRoot}/Documentation/scheduler.html"> for more information</a>
*  Copyright (c) 2009 Ecole Centrale de Nantes IRCCyN
* @author: richard
* @version
*/
package storm.Schedulers;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeMap;
import java.util.Stack;

import storm.EvtContext;
import storm.Processors.Processor;
import storm.Tasks.Task;


/**
 * @author richard
 *
 */
public class FP_M_FIFO_Scheduler extends Scheduler {

	/**
	 * Comparator pour trier sur la priorité
	 * utiliser pour ordonner les clefs de la treeMAP
	 * Plus grade priorité en tête
	 * @author richard
	 *
	 */
	class ordering_priority implements Comparator{
		/**
		 * 
		 */
		public int compare(Object arg0, Object arg1) {
			// TODO Auto-generated method stub
			Integer a0 = (Integer) arg0;
			Integer a1 = (Integer) arg1;
			if (a0.intValue() > a1.intValue()) return -1;
			else if (a1.intValue() == a0.intValue()) return 0;
			     else return 1;
		}
	}
	/**
	 * pour la fifo on utilise java.util.LinkedList
	 * avec ajout à la queue et retrait en tête
	 * Liste des priorité
	 */
	TreeMap listePriority;
	Boolean todo=false;
	/**
	 * 
	 */
	public void init() {
		/**
		 * Création de la liste de priorité
		 */
		super.init();
		this.listePriority = new TreeMap(new ordering_priority());
	}
	/**
	 * 
	 */
	public void onUnBlock(EvtContext c){
		Task T = (Task) c.getSource();
		addTask(T);
		todo = true;
	}
	/**
	 * 
	 */
	public void onBlock(EvtContext c){
		Task T = (Task) c.getCible();
		removeTask(T);
		todo = true;
	}
	/**
	 * 
	 * @param t
	 */
	private void removeTask(Task t) {
		// TODO Auto-generated method stub
		Integer A = new Integer(t.priority);
			/**
			 * clef connue
			 */
			LinkedList fifo =(LinkedList) this.listePriority.get(A);
			fifo.remove(t);
	}
	/**
	 * 
	 */
	public void onActivate(EvtContext c) {
		 /**
		  * Ajout de la priorité si elle n'existe pas
		  * Création de la FIFO si nécessaire
		  * et Ajout de la tache 
		  */
		Task T =(Task) c.getCible();
		addTask(T);
		todo=true;
	}
	/**
	 * 
	 * @param t
	 */
	private void addTask(Task t) {
		// TODO Auto-generated method stub
		//On regarde si la priorité existe
		Integer A = new Integer(t.priority);
		if (this.listePriority.containsKey(A)){
			//clef connue
			LinkedList fifo =(LinkedList) this.listePriority.get(A);
			fifo.addLast(t);
			
		}
		else{
			//clef inconnue
			LinkedList fifo = new LinkedList();
			fifo.addLast(t); //addlast pour fifo addfirst pour lifo
			this.listePriority.put(A,fifo );
			
		}
		
	}
	/**
	 * 
	 */
	public void sched() {
		// TODO Auto-generated method stub
		if (todo) select();
		todo=false;
	}
	/**
	 * 
	 */
	public void select() {
		Stack pileCPUS = new Stack();
		Stack pileTask = new Stack();
		// TODO Auto-generated method stub
		/**
		 * parcours de la liste des tâches
		 * Préemption des tâches au dela limite
		 * 
		 */
		ArrayList CPUS= this.Kernel.getTasksListeManager().getProcessors();
		int sizeCPUS= CPUS.size();
		Set allKey = this.listePriority.keySet();
		Iterator I= allKey.iterator();
		int k=0;
		/**
		 * Preemption
		 */
		while(I.hasNext()){
			Integer a = (Integer) I.next();
			LinkedList fifo = (LinkedList) this.listePriority.get(a);
			Iterator F=fifo.iterator();
			/**
			 * parcours liste des tâches
			 */
			while(F.hasNext()){
				Task T = (Task) F.next();
				/**
				 * toute les tâches audela du nombre de processeur ne peuvent pas
				 * utiliser un processeur. 
				 */
				if (k>=sizeCPUS){
					if (T.isIsrunning()){
						int d = T.getOwnFieldIntValue("nopreempt");
						if (d!=1) T.preempt();
					   }
				}
				k++;
			}
		}
		/**
		 * recherche CPUs libre
		 */
		for (int l=0; l< sizeCPUS ; l++){
			Processor p=(Processor)CPUS.get(l);
			if (!(p.isRunning())) pileCPUS.push(p);
		}
		/**
		 * Affectation des ressources de libre
		 */
		 I= allKey.iterator();
		 /**
		  * recherche d'autant de task not running que de processeur de libre
		  */
		 while(I.hasNext()) { 
			 /**
			  * ce n'est pas la peine de chercher plus de tâche que de processeur
			  */
			if (pileCPUS.size()==pileTask.size()) break;
			Integer a = (Integer) I.next();
			LinkedList fifo = (LinkedList) this.listePriority.get(a);
			Iterator F = fifo.iterator();
			while(F.hasNext()){
				Task T = (Task) F.next();
				if (!(T.isIsrunning())){
					pileTask.push(T);
				}
			}
		 }
		 /**
		  * affecte Task sur CPU libre
		  * Il peut y avoir autant ou moins de tâche que de CPU
		  * Si pas de tâche on sort
		  */
		 while((pileCPUS.size()>0)){
			 if (pileTask.size()==0) break;
			 Task T = (Task) pileTask.pop();
			 Processor C = (Processor) pileCPUS.pop();
			 T.runningOn(C);
		 }
	}
}
